﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WebServer.Models
{
    public class RouteCalculator
    {
        public Int32 CalculateNewRoute(DateTime from, DateTime to, String dropOffName)
        {
            using (StorageConnectorDataContext context = new StorageConnectorDataContext())
            {

                
                //Check if dropOff point exists
                DropOff dropOff = context.DropOffs.Where(d => d.Name == dropOffName).First();

                if (dropOff == null)
                {
                    throw new Exception("Drop off name not recognized.");
                }

                var transports = context.Transports.Where(t => from <= t.DropOffTime && t.DropOffTime <= to && t.DropOffLocation.Equals(dropOffName) && t.Serviced == false);

                return CalculateRoute(transports, dropOff);
            }
        }

        public Int32 CalculateUpdatedDirections(Int32 routeID)
        {
            using (StorageConnectorDataContext context = new StorageConnectorDataContext())
            {
                var transports = context.Transports.Where(t => t.RouteID == routeID);

                Route oldRoute = context.Routes.Where(d => d.ID == routeID).Single();

                if (transports.Count() > 0)
                {
                    DropOff dropOff = context.DropOffs.Single(dpf => dpf.Address.Equals(oldRoute.EndAddress));

                    Int32 newRouteID = CalculateRoute(transports, dropOff);

                    if (newRouteID > 0)
                    {
                        DeleteOldRoute(oldRoute.ID);

                        return newRouteID;
                    }
                }
            }

            return 0;
        }

        private void DeleteOldRoute(Int32 routeID)
        {
            using (StorageConnectorDataContext context = new StorageConnectorDataContext())
            {

                var route = context.Routes.Single(r => r.ID == routeID);
                if (route != null)
                {
                    context.Waypoints.DeleteAllOnSubmit(from w in context.Waypoints where w.RouteID == routeID select w);
                    context.Instructions.DeleteAllOnSubmit(from i in context.Instructions where i.RouteID == routeID select i);
                    context.SubmitChanges();
                    context.Routes.DeleteOnSubmit(route);
                    context.SubmitChanges();

                }
            }
        }

        private Int32 CalculateRoute(IEnumerable<Transport> transports, DropOff dropOff)
        {
            using (StorageConnectorDataContext context = new StorageConnectorDataContext())
            {
                if (transports.Count() > 0)
                {

                    DateTime dropOffTime = transports.OrderByDescending(t => t.DropOffTime).First().DropOffTime;

                    List<String> locations = (from t in transports select t.PickupAdress).ToList();



                    //Full implementation would include drivers start location, here it will just the same as the drop off
                    GoogleDirectionsParser parser = new GoogleDirectionsParser();

                    //Waiting time at each stop is 5min
                    if (parser.GetDirections(dropOff.Address, locations, dropOff.Address, new TimeSpan(0, 5, 0)))
                    {
                        Route route = new Route()
                        {
                            Distance = parser.totalDistance,
                            Duration = (int)parser.totalDuration.TotalMinutes,
                            EndAddress = parser.endAddress,
                            StartAddress = parser.startAddress,
                            ArrivalTime = dropOffTime,
                            GoogleMapsUrl = parser.mapUrl
                        };

                        context.Routes.InsertOnSubmit(route);
                        context.SubmitChanges();

                        if (route.ID != 0)
                        {
                            List<Instruction> instructions = new List<Instruction>();
                            for (int i = 0; i < parser.instructions.Count(); i++)
                            {
                                instructions.Add(new Instruction()
                                {
                                    ID = Guid.NewGuid(),
                                    RouteID = route.ID,
                                    Text = parser.instructions[i],
                                    NumberOnRoute = i + 1
                                });
                            }
                            context.Instructions.InsertAllOnSubmit(instructions);

                            List<Waypoint> waypoints = new List<Waypoint>();
                            for (int i = 0; i < parser.waypoints.Count(); i++)
                            {
                                DateTime arrival = dropOffTime
                                                .Add(parser.totalDuration.Negate())
                                                .Add(parser.waypointTimes[parser.waypoints[i]]);

                                waypoints.Add(new Waypoint()
                                {
                                    ID = Guid.NewGuid(),
                                    RouteID = route.ID,
                                    Address = parser.waypoints[i],
                                    EstimatedArrival = arrival,
                                    NumberStopOnRoute = i + 1
                                });
                            }
                            context.Waypoints.InsertAllOnSubmit(waypoints);

                            IEnumerable<Int32> transportIDs = from t in transports select t.ID;

                            var updatedTransports = from t in context.Transports where transportIDs.Contains(t.ID) select t;

                            foreach (Transport ts in updatedTransports)
                            {
                                
                                ts.RouteID = route.ID;
                                ts.Serviced = true;
                            }


                            context.SubmitChanges();

                            return route.ID;
                        }
                    }
                }
                else
                {
                    throw new Exception("No transport requests found.");
                }

                return 0;
            }
        }
    }

}